<!DOCTYPE html>
<!--
Copyright 2015 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->

<link rel="import" href="/perf_insights/mre/function_handle.html">
<link rel="import" href="/tracing/base/math/range.html">
<link rel="import" href="/tracing/model/helpers/chrome_model_helper.html">

<script>
'use strict';

tr.exportTo('pie', function() {
  // Collects the set of tasks that are preventing user input from being
  // processed on the main thread.
  // See https://goo.gl/l7V5xg.
  function mapInputBlockers(result, model) {
    var modelHelper = model.getOrCreateHelper(
        tr.model.helpers.ChromeModelHelper);
    var rendererHelpers = modelHelper.rendererHelpers;

    if (!rendererHelpers) {
      // If we didn't detect any renderer processes, bail out.
      result.addPair('inputBlockers', null);
      return;
    }

    // Look for main thread input handling in each renderer process.
    var inputBlockers = {};
    var foundInputBlockers = false;
    for (var pid in rendererHelpers) {
      var rendererHelper = rendererHelpers[pid];
      var mainThread = rendererHelper.mainThread;
      // Look for events that represent main thread input handling that also
      // have one associated flow event showing where the input came from.
      for (var event of mainThread.getDescendantEvents()) {
        if (event.title !== 'LatencyInfo.Flow' ||
            event.args['step'] !== 'HandleInputEventMain' ||
            event.inFlowEvents.length !== 1) {
          continue;
        }

        // Now we can derive the queueing interval from the flow event.
        var flowEvent = event.inFlowEvents[0];
        var queueRange =
            tr.b.math.Range.fromExplicitRange(flowEvent.start, event.start);

        // Find all events that intersect the queueing interval and compute how
        // much they contributed to it.
        for (var intersectingEvent of mainThread.getDescendantEvents()) {
          var eventRange =
              tr.b.math.Range.fromExplicitRange(intersectingEvent.start,
                  intersectingEvent.start + intersectingEvent.duration);
          var intersection = queueRange.findIntersection(eventRange);
          if (intersection.isEmpty || intersection.duration === 0)
            continue;
          if (inputBlockers[intersectingEvent.title] === undefined)
            inputBlockers[intersectingEvent.title] = [];
          inputBlockers[intersectingEvent.title].push(intersection.duration);
          foundInputBlockers = true;
        }
      }
    }

    if (!foundInputBlockers) {
      result.addPair('inputBlockers', null);
      return;
    }

    result.addPair('inputBlockers', inputBlockers);
  }

  pi.FunctionRegistry.register(mapInputBlockers);

  return {
    mapInputBlockersForTest: mapInputBlockers
  };
});
</script>
